//
//  TBBLEBase.m
//  TBSDK
//
//  Created by nexgo on 2017/10/27.
//  Copyright © 2017年 truth. All rights reserved.
//

#import "TBBLEBase.h"

// 状态
typedef enum{
    TBBLEBASE_FREE,        // 空闲状态
    TBBLEBASE_CONNECTING,  // 正在连接
    TBBLEBASE_DISCONNECTING, // 正在断开连接
    TBBLEBASE_WORKING,     // 正在工作
} TBBLEBaseStatus;

@interface TBBLEBase()<CBPeripheralDelegate>{
    
    TBBLEManager* manager;
    
    TBBLEDevice* device;  // 工作的设备
    int retryTimes;       // 重试次数
    TBBLEBaseCB* cb;   // 连接回调

    TBBLEBaseStatus status;

    TBBLEConnectCB* connectCB;  // 连接回调
    
    BOOL disConnectWithoutCbFlag;  // 静默断开
    
    VoidCB disConnectWithoutCb;
    
    NSMutableArray<TBBLEBaseAction*>* actions; // 任务队列
    TBBLEBaseAction* workAction;  // 正在工作的动作
    
}

@end


@implementation TBBLEBase

- (instancetype)init:(TBBLEManager*)managerTmp{
    self = [super init];
    if (self) {
        manager = managerTmp;
        actions = [[NSMutableArray alloc] init];
        status = TBBLEBASE_FREE;
        
        disConnectWithoutCbFlag = false;
        
        connectCB = [[TBBLEConnectCB alloc] init];
        
        __block TBBLEBase *blockSelf = self;
        connectCB.onConnect = ^(TBErrorCode* errorCode){
            [blockSelf onConnect:errorCode];
        };
        
        connectCB.onDisConnect = ^(TBErrorCode* errorCode){
            [blockSelf onDisConnect:errorCode];
        };
        
        connectCB.onManualDisConnect = ^(TBErrorCode* errorCode){
            [blockSelf onManualDisConnect:errorCode];
        };
        
    }
    return self;
}

/**
 开始连接
 @param deviceTmp 需要连接的设备
 @param retryTimesTmp 尝试重连次数
 @param cbTmp 回调
 @return 执行结果
 */
-(TBErrorCode*)connect:(TBBLEDevice*)deviceTmp retryTimes:(int)retryTimesTmp cb:(TBBLEBaseCB*)cbTmp{
    
    TBErrorCode* errorCode = [[TBErrorCode alloc] init];
    errorCode.code = TBOTHER;
    if(status != TBBLEBASE_FREE){  // 查看是否处于工作状态
        errorCode.msg = @"稍等片刻";
        return errorCode;
    }
    
    if([self isConnected]){  // 查看是否已经连接
        errorCode.msg = @"已存在连接";
        return errorCode;
    }
    
    device = deviceTmp;
    retryTimes = retryTimesTmp;
    cb = cbTmp;
    
    status = TBBLEBASE_CONNECTING;
    disConnectWithoutCbFlag = false;
    
    [self connectLoop];
    
    errorCode.code = TBSUCCESS;
    return errorCode;
}

-(void)connectLoop{
    
    if(status != TBBLEBASE_CONNECTING){  // 被主动取消掉了
        return;
    }
    
    if(retryTimes <= 0){   // 重试次数用完了，还没有连接成功
        status = TBBLEBASE_FREE;
        TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@"连接失败"];
        if((cb != nil) && (cb.onConnect != nil)){
            cb.onConnect(errorCode);
        }
        return;
    }
    
    retryTimes--;
    
    [manager connect:device connectCB:connectCB];
}

-(void)onConnect:(TBErrorCode*) errorcode{
    if(status != TBBLEBASE_CONNECTING){  // 处于非连接状态
        return;
    }
    
    if(errorcode.code != TBSUCCESS){   // 连接失败时
        [self connectLoop];
        return;
    }
    
    device.peripheral.delegate = self;
    
    [device.peripheral discoverServices:nil];  // 开始发现服务
}
-(void)onDisConnect:(TBErrorCode*) errorcode{
    
    if(status == TBBLEBASE_CONNECTING){  // 正在连接时断开了
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(connectSuccess) object:nil];
        if((connectCB != nil) && (connectCB.onConnect != nil)){
            status = TBBLEBASE_FREE;
            TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@"意外断开"];
            connectCB.onConnect(errorCode);
        }
        return;
    }
    
    status = TBBLEBASE_FREE;
    if((connectCB != nil) && (connectCB.onDisConnect != nil)){
        TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBSUCCESS msg:@""];
        connectCB.onDisConnect(errorCode);
    }
}
-(void)onManualDisConnect:(TBErrorCode*)errorcode{
    status = TBBLEBASE_FREE;
    if(disConnectWithoutCbFlag && (disConnectWithoutCb != nil)){  // 静默断开情况
        disConnectWithoutCb();
        return;
    }
    
    if((connectCB != nil) && (connectCB.onManualDisConnect != nil)){
        TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBSUCCESS msg:@""];
        connectCB.onManualDisConnect(errorCode);
    }
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error{
    if(status != TBBLEBASE_CONNECTING){  // 处于非连接状态
        return;
    }
    if(![self isConnected]){   // 过程中，被动断开了
        return;
    }
    if(error != nil){ // 发现服务错误
        [self __disConnect:^{
            if((connectCB != nil) && (connectCB.onConnect != nil)){
                TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@"发现服务错误"];
                connectCB.onConnect(errorCode);
            }
        }];
        return;
    }
    
    [self discovCharactersLoop];
}

-(void)discovCharactersLoop{
    if(status != TBBLEBASE_CONNECTING){  // 被主动取消掉了连接
        return;
    }
    if(![self isConnected]){   // 过程中，被动断开了
        return;
    }
    for (int i=0; i<device.peripheral.services.count; i++) {  // 寻找还未发现特征值的服务
        if(device.peripheral.services[i].characteristics == nil){
            [device.peripheral discoverCharacteristics:nil forService:device.peripheral.services[i]];
            return;
        }
    }
    
    [self runDelay:@selector(connectSuccess) withObject:nil afterDelay:0.5];  // 等待 0.5 秒，用来确保稳定连接
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error{
    if(error != nil){
        [self __disConnect:^{
            if((connectCB != nil) && (connectCB.onConnect != nil)){
                TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@"发现特征值错误"];
                connectCB.onConnect(errorCode);
            }
        }];
        return;
    }
    
    [self discovCharactersLoop];
}

-(void)connectSuccess{
    if(![self isConnected]){
        return;
    }
    
    status = TBBLEBASE_FREE;
    
    TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBSUCCESS msg:@""];
    if((cb != nil) && (cb.onConnect != nil)){  // 回调连接成功
        cb.onConnect(errorCode);
    }
}

/**
 断开连接
 */
-(TBErrorCode*)disConnect{
    
    if(status == TBBLEBASE_DISCONNECTING){
        TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@"已经正在断开"];
        return errorCode;
    }
    
    if(status == TBBLEBASE_FREE){   // 空闲时候
        
        if([self isConnected]){  // 并且此时已经连接了
            disConnectWithoutCbFlag = false;
            status = TBBLEBASE_DISCONNECTING;
            [manager disConnect:device];
            return nil;
        }
        status = TBBLEBASE_FREE;
        if((connectCB != nil) || (connectCB.onManualDisConnect != nil)){
            TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBSUCCESS msg:@""];
            connectCB.onManualDisConnect(errorCode);
            return nil;
        }
        return nil;
    }
    
    if(status == TBBLEBASE_CONNECTING){  // 正在连接的情况
        [manager disConnect:device];
        status = TBBLEBASE_FREE;
        if((connectCB != nil) || (connectCB.onManualDisConnect != nil)){
            TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBSUCCESS msg:@""];
            connectCB.onManualDisConnect(errorCode);
            return nil;
        }
        return nil;
    }
    
    return nil;
}


// 内部调用的断开连接，不会触发断开回调
-(BOOL)__disConnect:(VoidCB)disConnectCb{
    if(status == TBBLEBASE_DISCONNECTING){  // 已经调用过断开连接了
        return false;
    }
    
    if(![self isConnected]){   // 没有连接上时
        status = TBBLEBASE_FREE;
        disConnectCb();
        return true;
    }
    
    if(status == TBBLEBASE_CONNECTING){  // 正在连接时
        status = TBBLEBASE_FREE;
        disConnectCb();
        return true;
    }
    
    disConnectWithoutCbFlag = true;   // 已经连接情况下断开
    status = TBBLEBASE_DISCONNECTING;
    disConnectWithoutCb = disConnectCb;
    [manager disConnect:device];
    
    return nil;
}

/**
 查看连接状态
 */
-(BOOL)isConnected{
    if(device == nil){
        return false;
    }
    return ([device.peripheral state] == CBPeripheralStateConnected);
}
/**
 获取所有服务
 @return 服务列表
 */
-(NSArray<CBService*>*)getAllServices{
    
    if(![self isConnected]){  // 未连接的情况
        return nil;
    }
    
    return device.peripheral.services;
}

/**
 通过UUID，获取指定服务
 @param uuid 需要获取服务的uuid
 @return 执行结果
 */
-(CBService*)getServiceByUuid:(NSString*)uuid{
    
    if((![self isConnected]) || (uuid == nil)){
        return nil;
    }
    
    NSArray<CBService *> *services = device.peripheral.services;
    
    if(services == nil){
        return nil;
    }
    
    for (int i=0; i<services.count; i++) {  // 遍历一下列表，寻找对应uuid
        if([services[i].UUID.UUIDString isEqualToString:uuid]){
            return services[i];
        }
    }
    return nil;
}

/**
 获取指定character
 @param servicesUUID 对应服务的UUID
 @param characterUUID 对应特征值的UUID
 @return 执行结果
 */
-(CBCharacteristic*)getCharacterByUuid:(NSString*)servicesUUID characterUuid:(NSString*)characterUUID{
    
    if(characterUUID == nil){
        return nil;
    }
    
    CBService* service = [self getServiceByUuid:servicesUUID];
    
    if(service == nil){
        return nil;
    }
    
    NSArray<CBCharacteristic *>* characters = service.characteristics;
    
    if(characters == nil){
        return nil;
    }
    
    for (int i=0; i<characters.count; i++) {
        if([characters[i].UUID.UUIDString isEqualToString:characterUUID]){
            return characters[i];
        }
    }
    return nil;
}

/**
 使能特征值的监听
 @param character 对应服务
 @param enable 使能开关
 @return 执行结果
 */
-(TBErrorCode*)enCharacterListen:(CBCharacteristic*)character enable:(BOOL)enable{
    return nil;
}

/**
 添加任务
 @param action 添加的任务
 @return 执行结果
 */
-(TBErrorCode*)addAction:(TBBLEBaseAction*)action{
    
    if((![self isConnected]) && (status != TBBLEBASE_CONNECTING)){  // 设备已经连接，并且已经发现了服务
        TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@"未连接"];
        return errorCode;
    }
    
    [actions addObject:action];
    
    [self startActionLoop];
    return nil;
}

// 启动任务循环执行
-(void)startActionLoop{
    
    NSLog(@"startActionLoop");
    if(![self isConnected]){  // 已经断开连接的情况
        NSLog(@"1");
        return;
    }
    
    if(status != TBBLEBASE_FREE){  // 处于非空闲状态
        NSLog(@"2");
        return;
    }
    
    if((actions == nil) || (actions.count == 0)){  // 任务运行完毕
        NSLog(@"3");
        status = TBBLEBASE_FREE;
        return;
    }

    // 将已经完成的所有操作删除掉
    [self removeSomeAction];
    
    NSLog([NSString stringWithFormat:@"action size->%d", actions.count]);
    
    if(actions.count <= 0){  // 已经全部完成了
        status = TBBLEBASE_FREE;
        return;
    }
    
    workAction = actions[0];
    
    switch (workAction.actionType) {
        case TBBLEACTION_WRITECHACT:{  // 写特征值
            NSData* sendData = [((TBBLEWriteCharacterAction*)workAction) getPerData];
            if(sendData == nil){  // 这条数据发送完毕了
                workAction.status = TBBLEACTION_DONE;
                if((workAction.cb != nil) && (workAction.cb.onSuccess != nil)){
                    workAction.cb.onSuccess();
                }
                [self runDelay:@selector(startActionLoop) withObject:nil afterDelay:0]; // 重新跳转出来，防止出现递归现象
                return;
            }
            if((workAction.status == TBBLEACTION_WAITTING) && (workAction.cb != nil) && (workAction.cb.onStart != nil)){
                workAction.cb.onStart();
            }
            
            workAction.status = TBBLEACTION_RUNNING;
            if(workAction.cb != nil){  // 每次发送的数据回调
                TBBLEWriteCharacterActionCB* writeCharacterCb = (TBBLEWriteCharacterActionCB*)workAction.cb;
                if(writeCharacterCb.onSendPerData != nil){
                    writeCharacterCb.onSendPerData(sendData);
                }else{
                    NSLog(@"writeCharacterCb.onSendPerData == nil");
                }
            }
            
            CBCharacteristic* characters = ((TBBLEWriteCharacterAction*)workAction).character;
            if((characters.properties & CBCharacteristicPropertyWrite) != 0){
                [device.peripheral writeValue:sendData forCharacteristic:characters type:CBCharacteristicWriteWithResponse];
            } else {
                [device.peripheral writeValue:sendData forCharacteristic:characters type:CBCharacteristicWriteWithoutResponse];
            }
        }
            break;
        case TBBLEACTION_READCHACT:{   // 读特征值
            CBCharacteristic* character = ((TBBLEReadCharacterAction*)workAction).character;
            workAction.status = TBBLEACTION_RUNNING;
            
            if((workAction.cb != nil) && (workAction.cb.onStart != nil)){
                workAction.cb.onStart();
            }
            
            [device.peripheral readValueForCharacteristic:character];
            
            return;
        }
            break;
        case TBBLEACTION_READRSSI:{    // 读取rssi
            workAction.status = TBBLEACTION_RUNNING;
            if((workAction.cb != nil) && (workAction.cb.onStart != nil)){
                workAction.cb.onStart();
            }
            [device.peripheral readRSSI];
        }
            break;
        case TBBLEACTION_ENCHARACTERNOTIFY:{  // 使能收数据监听
            workAction.status = TBBLEACTION_RUNNING;
            TBBLEEnNotifyAction* enNotifyAction = ((TBBLEEnNotifyAction*)workAction);
            
            if((enNotifyAction.cb != nil) && (enNotifyAction.cb.onStart != nil)){
                enNotifyAction.cb.onStart();
            }
            
            if(enNotifyAction.character.isNotifying == enNotifyAction.enable){  // 如果本身就是这个状态，则不需要操作
                enNotifyAction.status = TBBLEACTION_DONE;
                if((enNotifyAction.cb != nil) && (enNotifyAction.cb.onSuccess != nil)){
                    enNotifyAction.cb.onSuccess();
                }
                [self runDelay:@selector(startActionLoop) withObject:nil afterDelay:0];
                return;
            }
            [device.peripheral setNotifyValue:enNotifyAction.enable forCharacteristic:enNotifyAction.character];
            
        }
            break;
        default:
            break;
    }
    
}

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error{
    
    if(![self isConnected]){
        return;
    }
    
    if(workAction.status != TBBLEACTION_RUNNING){
        return;
    }
    if(workAction.actionType != TBBLEACTION_WRITECHACT){  // 类型不对的情况
        return;
    }
    
    TBBLEWriteCharacterAction* action = (TBBLEWriteCharacterAction*)workAction;
    if(error != nil){
        if((action.cb != nil) && (action.cb.onFail)){
            TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@"发送失败"];
            action.status = TBBLEACTION_FAIL;
            action.cb.onFail(errorCode);
        }
        return;
    }
    if((action.character.properties & CBCharacteristicPropertyWrite) != 0){ // 无反馈写入时，需要有一定的延迟
        [self runDelay:@selector(startActionLoop) withObject:nil afterDelay:0];  // 防止出现递归
    } else {
        [self runDelay:@selector(startActionLoop) withObject:nil afterDelay:((float)action.option.waitTime)/1000];  // 防止出现递归
    }
    
}

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error{
    if(workAction.actionType == TBBLEACTION_READCHACT){  // 处于读特征值操作下
        TBBLEReadCharacterAction* action = (TBBLEReadCharacterAction*)workAction;
        if(action.status == TBBLEACTION_RUNNING){
            if(action.character == characteristic){  // 确实是读取数据
                if(error == nil){
                    if(action.cb != nil){
                        TBBLEReadCharacterActionCB* readCB = (TBBLEReadCharacterActionCB*)action.cb;
                        action.status = TBBLEACTION_FAIL;
                        if(readCB.onReadData != nil){
                            readCB.onReadData(characteristic.value);
                        }
                    }
                } else {
                    if(action.cb != nil){
                        TBBLEReadCharacterActionCB* readCB = (TBBLEReadCharacterActionCB*)action.cb;
                        action.status = TBBLEACTION_DONE;
                        if(readCB.onFail != nil){
                            TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@"读取失败"];
                            readCB.onFail(errorCode);
                        }
                    }
                }
                [self runDelay:@selector(startActionLoop) withObject:nil afterDelay:0];
                return;
            }
        }
    }
    
    // 如果不是读取数据，而是notify
    if((cb != nil) && (cb.onCharactChanged != nil)){
        cb.onCharactChanged(characteristic, characteristic.value);
    }
    
}

- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error{
    if((workAction.actionType != TBBLEACTION_READRSSI) || (workAction.cb == nil)){
        return;
    }
    TBBLEReadRssiActionCB* readRssiCB = (TBBLEReadRssiActionCB*)workAction.cb;
    if(readRssiCB.onReadRssi == nil){
        return;
    }
    readRssiCB.onReadRssi(RSSI.intValue);
}

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error{
    if((workAction.actionType != TBBLEACTION_ENCHARACTERNOTIFY) || (workAction.cb == nil)){
        return;
    }
    if(error != nil){
        if(workAction.cb.onFail != nil){
            TBErrorCode* errorCode = [[TBErrorCode alloc] init:TBOTHER msg:@""];
            workAction.cb.onFail(errorCode);
        }
    }else{
        if(workAction.cb.onSuccess != nil){
            workAction.cb.onSuccess();
        }
    }
    
    [self runDelay:@selector(startActionLoop) withObject:nil afterDelay:0];
}

// 移除已完成，错误，被取消的操作
-(void)removeSomeAction{
    NSMutableArray<TBBLEBaseAction*>* needRemoveActions = [[NSMutableArray alloc] init];
    for (int i=0; i<actions.count; i++) {
        if((actions[i].status == TBBLEACTION_DONE) || (actions[i].status == TBBLEACTION_FAIL) || (actions[i].status == TBBLEACTION_CANCEL)){
            [needRemoveActions addObject:actions[i]];
        }
    }
    [actions removeObjectsInArray:needRemoveActions];
}

// 切换到主线程
-(void) runDelay:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay{
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self performSelector:aSelector withObject:anArgument afterDelay:delay];
        });
        
        return;
    }else{
        [self performSelector:aSelector withObject:anArgument afterDelay:delay];
    }
}

@end


// 连接回调
@implementation TBBLEBaseCB
@end
